package com.ljy.my_study.spring.循环依赖.core;

import com.ljy.my_study.spring.循环依赖.annotation.MyAutowired;
import com.ljy.my_study.spring.循环依赖.annotation.MyComponent;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

public class MyBeanFactory implements IBeanFactory{

    private Map<Class,MyBeanDefinition> beanDefinitionMap;

    private Map<Class,MyBeanDefinition> initingBeanDefinition;

    public MyBeanFactory() {
        this.beanDefinitionMap = new HashMap<>();
        this.initingBeanDefinition=new HashMap<>();
    }

    @Override
    public <T> T getBean(Class<T> clazz) throws IllegalAccessException, InstantiationException {
        MyBeanDefinition myBeanDefinition=beanDefinitionMap.get(clazz);
        if(myBeanDefinition==null){
            return null;
        }
        if(myBeanDefinition.getT()!=null){
            return (T) myBeanDefinition.getT();
        }
        T t=clazz.newInstance();
        myBeanDefinition.setT(t);
        Iterator<String> keyIt=myBeanDefinition.getFieldMap().keySet().iterator();
        while(keyIt.hasNext()){
            String key=keyIt.next();
            FieldBeanDefinition fieldBeanDefinition= (FieldBeanDefinition) myBeanDefinition.getFieldMap().get(key);
            T fieldT= (T) fieldBeanDefinition.getFieldBeanDefinition().getT();
            if(fieldT==null){
                Field field=fieldBeanDefinition.getF();
                field.setAccessible(true);
                field.set(t,getBean(field.getType()));
            }else{
                Field field=fieldBeanDefinition.getF();
                field.setAccessible(true);
                field.set(t,fieldT);
            }
        }
        return t;
    }

    public void scan(String url) throws ClassNotFoundException {
//        File file=new File(url);
//        File file=new File(this.getClass().getResource("").getPath()+"\\"+"bean");
//        File[] subFiles=file.listFiles();
        ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
//        for(File f:subFiles){
//            String s=f.getName();
//            createMyBeanDefinition(classLoader.loadClass(url+"."+s));
//        }

        String[] array={"com.ljy.my_study.spring.循环依赖.bean.A"
                ,"com.ljy.my_study.spring.循环依赖.bean.B"
                ,"com.ljy.my_study.spring.循环依赖.bean.C"};
        for(String s:array){
            createMyBeanDefinition(classLoader.loadClass(s));
        }

        if(!initingBeanDefinition.isEmpty()){
            for(Map.Entry<Class, MyBeanDefinition> e:initingBeanDefinition.entrySet()){
                createMyBeanDefinition(e.getKey());
            }
        }
    }

    public <T> MyBeanDefinition createMyBeanDefinition(Class<T> clazz) throws ClassNotFoundException {

        MyBeanDefinition beanDefinition=beanDefinitionMap.get(clazz);
        if(beanDefinition==null){
            beanDefinition=new MyBeanDefinition();
            beanDefinition.setState(1);
        }else {
            if(beanDefinition.getState()==1&&!initingBeanDefinition.containsKey(clazz)){
                initingBeanDefinition.put(clazz,beanDefinition);
                return beanDefinition;
            }else if(beanDefinition.getState()==2){
                return beanDefinition;
            }
        }
        beanDefinitionMap.put(clazz,beanDefinition);

        Annotation myComponent=clazz.getAnnotation(MyComponent.class);
        if(myComponent==null){
            return null;
        }

        if(!initingBeanDefinition.containsKey(clazz)){
            beanDefinition.setBeanName(clazz.getName());
        }

        Field[] fields=clazz.getDeclaredFields();
        beanDefinition.setFieldMap(new HashMap<>());
        for(Field f:fields){
            MyAutowired myAutowired=f.getAnnotation(MyAutowired.class);
            if(myAutowired==null){
                continue;
            }
            Class fClazz=f.getType();
            MyBeanDefinition fieldMyBeanDefinition=createMyBeanDefinition(fClazz);
            FieldBeanDefinition fieldBeanDefinition=new FieldBeanDefinition();
            fieldBeanDefinition.setF(f);
            fieldBeanDefinition.setKey(f.getName());
            fieldBeanDefinition.setFieldBeanDefinition(fieldMyBeanDefinition);
            beanDefinition.getFieldMap().put(f.getName(),fieldBeanDefinition);
        }
        beanDefinition.setState(2);
//        initingBeanDefinition.remove(clazz);
        return beanDefinition;
    }
}
